//****************************************************************************************
// Name:		proc.cpp
// Platform:	SQL Server 2000 SP3a or higher, Windows NT, 2000 or XP
// Author:		Copyright (c) 2006 by Michael Coles, MCDBA
//
// Description:	Implements the xp_decrypt_blowfish XP.  Based on source code by
//				Bruce Schneier.
//
// LEGAL STUFF:
// ------------
// Copyright (C) 2005 - 2006 by Michael Coles, MCDBA
//
// Some included code included is released under the redistribution agreements as 
// specified by the authors of the respective code.  Copyright holders of this included 
// code maintain all copyright and other rights to their original or derivative works.
//
// All rights reserved.                          
//
// REDISTRIBUTION OF THIS CODE:
// ----------------------------
// All code included in this package is either the original work of the copyright holder,
// or derivative work based on other copyright holders' works.  All derivative works 
// include information as required by the copright holders' redistribution agreements.
// These redistribution agreements, where possible, are included in the text of the source
// code distributed with this code.
//
// Redistribution and use in source and binary forms, with or without modification, are 
// permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list 
//      of conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this 
//      list of conditions and the following disclaimer in the documentation and/or other 
//      materials provided with the distribution.
//
//   3. The names of its contributors may not be used to endorse or promote products 
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT 
// SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
// WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//****************************************************************************************
#include <dblib.h>
#include <stdafx.h>
#include <constants.h>
#include <params.h>
#include <twofish.h>
#include <sha2.h>
#include <aes.h>
#include <stdexcept>

#ifdef __cplusplus
extern "C" {
#endif

RETCODE __declspec(dllexport) xp_decrypt_twofish(SRV_PROC *srvproc);

#ifdef __cplusplus
}
#endif

RETCODE __declspec(dllexport) xp_decrypt_twofish(SRV_PROC *srvproc)
{

	RETCODE rc = XP_NOERROR;
	params *P1 = new params();	// plain text
	params *P2 = new params();	// encrypted text OUTPUT
	params *P3 = new params();	// password used to encrypt master-key
	params *P4 = new params();	// encrypted master key
	params *P5 = new params();	// encrypted local key
	params *P6 = new params();	// key size

	BYTE password[128];
	BYTE passhash[64];
	ULONG passwordlength = 0;
	BYTE *localkey = new BYTE[256];
	int localkeylength = 0;
	BYTE *masterkey = new BYTE[256];
	int masterkeylength = 0;
	BYTE *localkeydecrypt = new BYTE[64];
	BYTE *plaintext = NULL;
	ULONG plaintextlength = 0;
	UINT keybits = 32; // 256 bit key by default

	try
	{
		// *** First, the set-up.  Here we grab all parameters passed in and verify their
		// *** sizes, total count and other aspects.

		int numparams = params::getparamcount(srvproc);
		if (numparams != 6) { // Requires six parameters exactly
			Dblib::printerror(srvproc, USAGE_DECRYPT_TWOFISH);
			rc = XP_ERROR;
		}
		if (rc == XP_NOERROR) {
			params::getparam(srvproc, 1, P1);
			params::getparam(srvproc, 2, P2);
			params::getparam(srvproc, 3, P3);
			params::getparam(srvproc, 4, P4);
			params::getparam(srvproc, 5, P5);
			params::getparam(srvproc, 6, P6);
			if (P1->isoutput || !P2->isoutput || P3->isoutput || P4->isoutput || P5->isoutput || P6->isoutput) { 
				Dblib::printerror(srvproc, USAGE_DECRYPT_TWOFISH);
				rc = XP_ERROR;
			} else if (!P3->isnull) { // If parameter is not NULL, grab the third param
				srv_bzero(password, 128);
				memcpy(password, P3->cdata, P3->length);
				passwordlength = P3->length;
			} else { // If third parameter is NULL
				srv_bzero(password, 128);
				strcpy((char *)password, srv_pfield(srvproc, SRV_USER, (int *)NULL));
				strupr((char *)password);
				passwordlength = (int)strlen((char *)password);
			}
		}
		if (rc == XP_NOERROR) {
			if (P6->isnull) {
				keybits = 32;
			} else {
				ULONG *i = (ULONG *)P6->cdata;
				if (*i >= 32 && *i <= 448 && (*i % 8 == 0)) {
						keybits = (*i / 8);
				} else {
					Dblib::printerror(srvproc, ERR_BAD_KEY_BITS);
					rc = XP_ERROR;
				}
			}
		}
		if (rc == XP_NOERROR) {
			if (P2->maxlength < P1->length + 16) {
				Dblib::printerror(srvproc, ERR_OUTPUT_LENGTH);
				rc = XP_ERROR;
			} else if (P1->isnull || P4->isnull || P5->isnull) {
				rc = XP_ERROR;
			} else {
				sha512_ctx ctx[1];	// Create an SHA-512 hash
				sha512_begin(ctx);
				sha512_hash(password, passwordlength, ctx);
				sha512_end(passhash, ctx);
				plaintext = new BYTE[P1->length + 16];
				memcpy(plaintext, P1->cdata, P1->length);
				*(plaintext + P1->length) = '\0';
				plaintextlength = P1->length;
				if (P4->length >= 64) {
					memcpy(masterkey, P4->cdata, 64);
					masterkeylength = 64;
				}
				if (P5->length >= 80) {
					memcpy(localkey, P5->cdata, 80);
					localkeylength = 80;
				}
				if (masterkeylength == 0 || localkeylength == 0) {
					Dblib::printerror(srvproc, ERR_KEY_LENGTH);
					rc = XP_ERROR;
				}
			}
		}

		// *** Here's the meat and potatoes.  First we decrypt the master key using CryptoAPI,
		// *** then we decrypt the local key using the master key.  Finally we encrypt the
		// *** plain text using the local key.

		if (rc == XP_NOERROR) {
			HCRYPTPROV hProv = NULL;
			if(!CryptAcquireContext(	// Grab a Crypto API Context
				&hProv,					// handle to the CSP
				SCRYPTO_KEY_CONTAINER,	// container name 
				NULL,					// use the default provider
				PROV_RSA_FULL,			// provider type
				CRYPT_MACHINE_KEYSET))	// flag values
			{
				Dblib::printerror(srvproc, ERR_CRYPTOAPI_CONTAINER);
				rc = XP_NOERROR;
			} else {
				DWORD dwSize = 64;
				if (rc == XP_NOERROR) {
					HCRYPTKEY hKey = NULL;
					HCRYPTHASH hHash = NULL;
					// Create a hash object.
					if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) {
						// Create a CryptoAPI hash object
						Dblib::printerror(srvproc, ERR_CRYPTOAPI_CREATEHASH);
						rc = XP_ERROR;
					}
					if (rc == XP_NOERROR && !CryptHashData(hHash, (CONST BYTE *)passhash, 64, 0)) {
						// Create a CryptoAPI hash of the password
						Dblib::printerror(srvproc, ERR_CRYPTOAPI_SHA1);
						rc = XP_ERROR;
					} 
					if (rc == XP_NOERROR && !CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &hKey)) {
						// Derive a session key using the Crypto API
						Dblib::printerror(srvproc, ERR_CRYPTOAPI_DERIVE_KEY);
						rc = XP_ERROR;
					}
					if (rc == XP_NOERROR) {
						// Set variable to length of data in buffer.
						dwSize = 64;
						// Now decrypt data.
						if (!CryptDecrypt(			// Perform the actual decryption
									hKey,			// Key obtained earlier
									0,				// No hashing of data
									TRUE,			// Final or only buffer of data
									0,              // Must be zero
									masterkey,		// Data buffer
									&dwSize)) 		// Size of data
						{					
							Dblib::printerror(srvproc, ERR_CRYPTOAPI_DEC_MASTER);
							rc = XP_ERROR;
						}
						// Clean up
						CryptDestroyKey(hKey);
						CryptDestroyHash(hHash);
						CryptReleaseContext(hProv, 0);
					}
					if (rc == XP_NOERROR) {
						// Here we decrypt the local key using the master key
						Rijndael rin;
						rin.init(Rijndael::CBC, Rijndael::Decrypt, masterkey, Rijndael::Key32Bytes);
						int len = rin.padDecrypt(localkey, 80, localkeydecrypt);
						// And finally, we encrypt our plain text using the local key
						twofish tf;
						tf.set_key(localkeydecrypt, keybits*8, twofish::dec);
						tf.decrypttext(plaintext, plaintextlength);
						tf.Unpad(plaintext, plaintextlength);
					}
				}
			}
		}
	} catch(std::exception) {
		Dblib::printerror(srvproc, ERR_CRYPT_EXCEPTION);
		rc = XP_ERROR;
	}
	// *** Send the output via the OUTPUT parameter back to SQL
	if (rc == XP_NOERROR) {
		srv_paramsetoutput(srvproc, 2, plaintext, plaintextlength, FALSE);
	} else {
		srv_paramsetoutput(srvproc, 2, (BYTE *)"", 0, TRUE);
	}
	srv_senddone(srvproc, SRV_DONE_MORE, (DBUSMALLINT)0, (DBINT)1);
	// *** And a little clean-up
	if (plaintext != NULL) {
		srv_bzero(plaintext, plaintextlength);
		delete [] plaintext;
	}
	plaintext = NULL;
	if (localkey != NULL) {
		srv_bzero(localkey, localkeylength);
		delete [] localkey;
	}
	localkey = NULL;
	if (masterkey != NULL) {
		srv_bzero(masterkey, masterkeylength);
		delete [] masterkey;
	}
	masterkey = NULL;
	if (localkeydecrypt != NULL) {
		srv_bzero(localkeydecrypt, 64);
		delete [] localkeydecrypt;
	}
	localkeydecrypt = NULL;
	srv_bzero(password, 128);
	srv_bzero(passhash, 64);
	if (P1 != NULL)
		delete P1;
	P1 = NULL;
	if (P2 != NULL)
		delete P2;
	P2 = NULL;
	if (P3 != NULL)
		delete P3;
	P3 = NULL;
	if (P4 != NULL)
		delete P4;
	P4 = NULL;
	if (P5 != NULL)
		delete P5;
	P5 = NULL;
	if (P6 != NULL)
		delete P6;
	P6 = NULL;
	return rc;
}

